/*
 * DbUtil.cpp
 *
 *  Created on: 2018年1月3日
 *      Author: zhengboyuan
 */

#include "DbUtil.h"
#include "CLog.h"
#include "DbSource.h"
#include "DbExecption.h"
#include "TStringUtil.h"

#ifndef WIN32
    #include <unistd.h>
#endif //

#include "SociType.h"



namespace db
{

DbUtil::DbUtil()
{
}

DbUtil::~DbUtil()
{
}

std::string DbUtil::getBackendName(soci::session& session)
{
	std::string source;
    try
    {
        if (session.get_backend() == NULL)
        {
            session.reconnect();
        }

        source = session.get_backend_name();
    }
    catch (soci::soci_error& ex)
    {
    	CLog::error("getBackendName. soci error: %s.\n", ex.what());
        return "";
    }
    return source;
}

bool DbUtil::validate(soci::session& session, DbSourceType dbType, int& category, std::string& err)
{
	bool done = false;

	try
	{
		if (session.get_backend() == NULL)
		{
			session.reconnect();
		}

		std::string name = session.get_backend_name();
		DbSource::parse(name.c_str(), dbType);
	}
	catch (soci::soci_error& ex)
	{
		CLog::error("validate. category: %d, soci error: %s\n", ex.get_error_category(), ex.what());

		category = ex.get_error_category();
		err = ex.what();

		return false;
	}

	if (dbType == DB_MYSQL)
	{
		done = validateMysql(session);
	}
	else if (dbType == DB_ODBC)
	{
		done = validateOdbc(session);
	}
	else if (dbType == DB_SQLITE)
	{
		done = true;
	}
	else
	{
		done = true;
	}

	return done;
}

bool DbUtil::isCrashed(DbSourceType dbType, int category, const std::string& err)
{
	if (dbType == DB_MYSQL)
	{
		return comn::StringUtil::startsWith(err, "SSL connection error");
	}
	return false;
}

bool DbUtil::validate(soci::session& session, DbSourceType dbType)
{
	int category = soci::soci_error::unknown;
	std::string err;

    bool done = validate(session, dbType, category, err);
    return done;
}

bool DbUtil::validateMysql(soci::session& session)
{
    bool done = false;
    for (int i = 0; i < 2; i ++)
    {
        try
        {
            session << "select 1";
            done = true;
        }
        catch (soci::mysql_soci_error& ex)
        {
            CLog::error("mysql error: %s\n", ex.what());

            if (reconnect(session))
            {
                continue;
            }
        }
        catch(soci::soci_error& ex)
        {
            CLog::error("validateMysql. soci error: %s\n", ex.what());
        }

        break;
    }
    return done;
}

bool DbUtil::validateOracle(soci::session& session)
{
    bool done = false;
    for (int i = 0; i < 2; i ++)
    {
        try
        {
            soci::row row;
            session << "select * from dual", soci::into(row);
            done = true;
        }
        catch (soci::oracle_soci_error& ex)
        {
            //if (ex.err_num_ == soci::oracle::kDisconneted)
            if (reconnect(session))
            {
                continue;
            }
            else
            {
                CLog::error("oracle error: num:%d, %s\n", ex.err_num_, ex.what());
            }
        }
        catch(soci::soci_error& ex)
        {
            CLog::error("validateOracle. soci error: %s\n", ex.what());
        }

        break;
    }
    return done;
}

bool DbUtil::validateOdbc(soci::session& session)
{
    bool done = false;
    for (int i = 0; i < 2; i ++)
    {
        try
        {
            soci::row row;
            session << "select * from dual", soci::into(row);
            done = true;
            break;
        }
        catch(soci::soci_error& ex)
        {
            if (reconnect(session))
            {
                continue;
            }
            CLog::error("validateOdbc. soci error: %s\n", ex.what());
            continue;
        }
    }
    return done;
}



bool DbUtil::get(soci::row& r, const char* name, std::string& value)
{
    try
    {
        value = r.get< std::string >(name, std::string());
        return true;
    }
    catch (soci::soci_error& ex)
    {
        CLog::error("DbUtil::get soci error: %s\n", ex.what());
        return false;
    }
    catch (std::runtime_error& ex)
    {
        CLog::error("runtime_error: %s\n", ex.what());
        return false;
    }
}

bool DbUtil::get(soci::row& r, const char* name, int& value)
{
    try
    {
        soci::column_properties props = r.get_properties(name);
        if (props.get_data_type() == soci::dt_long_long)
        {
            value = (int)r.get< long long >(name, 0);
        }
        else if (props.get_data_type() == soci::dt_unsigned_long_long)
        {
            value = (int)r.get< unsigned long long >(name, 0);
        }
        else
        {
            value = (int)r.get< int >(name, 0);
        }
        return true;
    }
    catch (std::runtime_error& ex)
    {
        CLog::error("runtime_error: %s\n", ex.what());
        return false;
    }
}

bool DbUtil::getShortTime(soci::row& r, const char* name, time_t& value)
{
    try
    {
        struct tm tmCur;
        memset(&tmCur, 0, sizeof(tmCur));
        tmCur = r.get<tm>(name);

        tmCur.tm_isdst = 0;
        if (tmCur.tm_year < 0)
        {
            tmCur.tm_year = 100;
        }

        value = mktime(&tmCur);
        return true;
    }
    catch (std::runtime_error& ex)
    {
        CLog::error("runtime_error: %s\n", ex.what());
        return false;
    }
}

bool DbUtil::getDateTime(soci::row& r, const char* name, time_t& value)
{
    try
    {
        struct tm tmCur;
        memset(&tmCur, 0, sizeof(tmCur));
        tmCur = r.get<tm>(name);

        value = mktime(&tmCur);

        return true;
    }
    catch (std::runtime_error& ex)
    {
        CLog::error("runtime_error: %s\n", ex.what());
        return false;
    }
}


std::string DbUtil::formatTime(time_t t)
{
    if (t == 0)
    {
        t = 3600 * 24;
    }

    struct tm tmObj;
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
    localtime_s(&tmObj, &t);
#else
    tmObj = *localtime(&t);
#endif //

    char buffer[512] = {0};
    strftime(buffer, 512, "%Y-%m-%d %H:%M:%S", &tmObj);
    return buffer;
}

bool DbUtil::reconnect(soci::session& session)
{
    try
    {
        session.reconnect();
        return true;
    }
    catch (soci::soci_error& ex)
    {
        CLog::error("Connection::reconnect. soci error: %s\n", ex.what());
        return false;
    }
}

std::string DbUtil::makeInSql(const char* col, const IntArray& values)
{
    std::ostringstream ss;
    ss << col << " in ";
    ss << '(';

    for (size_t i = 0; i < values.size(); ++ i)
    {
        if (i != 0)
        {
            ss << ',';
        }
        ss << values[i];
    }

    ss << ')';
    return ss.str();
}

std::string DbUtil::makeInSql(const char* col, const StringArray& values)
{
    std::ostringstream ss;
    ss << col << " in ";
    ss << '(';

    for (size_t i = 0; i < values.size(); ++i)
    {
        if (i != 0)
        {
            ss << ',';
        }
        ss << values[i];
    }

    ss << ')';
    return ss.str();
}

void DbUtil::sleepMs(unsigned int ms)
{
#ifdef WIN32
    Sleep(ms);
#else
    usleep(ms * 1000);
#endif
}

bool DbUtil::isSqlite(soci::session& session)
{
    return DbUtil::getBackendName(session) == DbSource::SQLITE3;
}

std::string DbUtil::escapePath(const std::string& src)
{
    std::string str;
    str.reserve(src.size() + 16);

    for (char ch : src)
    {
        str += ch;

        if (ch == '\\')
        {
            str += ch;
        }
    }
    return str;
}

std::string DbUtil::escape(const std::string& src)
{
	std::string chars("'\"\\\n\t\r\\%");

	std::string str;
	str.reserve(src.size() + 24);

	for (char ch : src)
	{
		if (chars.find_first_of(ch) != std::string::npos)
		{
			str += '\\';
		}

		str += ch;
	}
	return str;
}





} /* namespace db */
